function Editor(){
    this.$initEventable();
    this.$initRenderable();
    this.m_textModel = new TextModel();
    this.m_textView = new TextView(this.m_textModel);
    this.m_undoManager = new UndoManager(this.m_textModel, this.selection());
    this.$on(this.m_textView, 'mousedown', this._onMouseDown);
    this.$on(this.m_textView, 'click', this._onClick);
    this.$on(this.m_textView, 'selectionchange', this._onSelectionChange);
    this.m_cursor = new Cursor_();
    this.m_textInput = new TextInput_();
    this.$on(this.m_textInput, 'textinput', this._onTextInput);
    this.$on(this.m_textInput, 'blur', this._onBlur);
    this.$on(this.m_textInput, 'keydown', this._onKeyDown);
    this.m_inputSessionTimer = null;
    this.m_preventSave = false;
}
Editor.prototype = {
    _TPL_ELM: '<div id="#{id}" class="QEEditor">#{textViewHTML}</div>',
    getText: function (){
        return this.m_textModel.getText();
    },
    setText: function (text){
        this.m_textModel.setText(text);
        this.m_undoManager.save();
    },
    selection: function (){
        return this.m_textView.selection();
    },
    isFocused: function (){
        return this.m_textInput.isFocused();
    },
    $renderHTML: function (){
        return Utils.format(this._TPL_ELM, {
            id: this.$id(),
            textViewHTML: this.m_textView.$renderHTML()
        });
    },
    $postRender: function (){
        this.m_textView.$postRender();
        this.m_cursor.render(this.m_textView.$contentElm());
        this.m_textInput.render(this.m_textView.$contentElm());
        this.$on(this.$elm(), 'dragover', this._onDragOver);
        this.$on(this.$elm(), 'drop', this._onDrop);
    },
    _onMouseDown: function (evt){
        if (!this.isFocused()) {
            this.m_textInput.setPosition({
                x: this.m_textView.scrollLeft(),
                y: this.m_textView.scrollTop()
            });
            this.m_textInput.focus();
        }
        DOMUtils.preventDefault(evt);
    },
    _onClick: function (){
//        if (!this.isFocused()) {
//            this.m_textInput.setPosition({
//                x: this.m_textView.scrollLeft(),
//                y: this.m_textView.scrollTop()
//            });
//            this.m_textInput.focus();
//        }
    },
    _onSelectionChange: function (){
        if (!this.isFocused()) return;
        var offset = this.selection().focusOffset();
        var lineIndex = this.m_textModel.getLineIndex(offset);
        var cursorPosition = this.m_textView.getPosition(offset);
        this.m_cursor.setCursor(cursorPosition,
            this.m_textView.getLineHeight(lineIndex));
        this.m_cursor.restart();
        this.m_textInput.setPosition(cursorPosition);
        if (!this.m_preventSave) {
            this.m_undoManager.save();
        }
    },
    _onTextInput: function (evt){
        this.$insert(evt.text);
        if (!this.m_inputSessionTimer) {
            this._startInputSession();
        }
        clearTimeout(this.m_inputSessionTimer);
        var self = this;
        this.m_inputSessionTimer = setTimeout(function (){
            self._endInputSession();
        }, 1000);
    },
    _onKeyDown: function (evt){
        // todo: 改成KeyBinding模式实现
//        console.log(evt.keyCode);
        switch (evt.keyCode) {
        case 8: // BACKSPACE
            this.$doBackspace();
            break;
        case 9: // TAB
            this.$doTab();
            break;
        case 13: // ENTER
            this.$doEnter();
            break;
        case 90: // CTRL+Z
            if (evt.ctrlKey) {
                this.$doUndo();
            }
            break;
        default:
            return;
        }
        DOMUtils.preventDefault(evt);
    },
    _onBlur: function (){
        this.m_cursor.hide();
        this.m_cursor.stop();
    },
    _onDragOver: function (evt){
        return DOMUtils.preventDefault(evt);
    },
    _onDrop: function (evt){
        var file;
        try {
            file = evt.dataTransfer.files[0];
        } catch(err) {
            return DOMUtils.preventDefault(evt);
        }
        var self = this;
        if (window.FileReader) {
            var reader = new FileReader();
            reader.onload = function() {
                self.m_textModel.setText(reader.result);
            };
            reader.readAsText(file);
        }

        return DOMUtils.preventDefault(evt);
    },
    _startInputSession: function (){
        this.m_inInputSession = true;
        this.dispatchEvent({ type: 'inputsessionstart' });
        this.m_undoManager.save();
    },
    _endInputSession: function (){
        this.m_inInputSession = false;
        this.dispatchEvent({ type: 'inputsessionend' });
        this.m_undoManager.save();
    },
    $insert: function (text){
        this.m_preventSave = true;
        var selection = this.selection();
        var offset = this.m_textModel.replaceText(
            selection.startOffset(),
            selection.endOffset() - selection.startOffset(),
            text);
        selection.setOffset(offset);
        this.m_preventSave = false;
    },
    $doBackspace: function (){
        var selection = this.selection();
        if (selection.isCollapsed()) {
            selection.moveLeft(true);
        }
        this.$insert('');
    },
    $doTab: function (){
        this.$insert('\t');
    },
    $doEnter: function (){
        this.$insert('\n');
    },
    $doUndo: function (){
        this.m_undoManager.undo();
    }
};

Utils.extend(Editor, Eventable);
Utils.extend(Editor, Renderable);